package com.jichangxiu.utils.git;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class GitUtils {

    /**
     * 在本地创建一个 Git 仓库
     *
     * @param repositoryPath 本地仓库路径（路径最后一层可以是不存在的）
     * @return 仓库对象
     * @throws IOException io异常
     */
    public static Repository createNewRepository(String repositoryPath) throws IOException {
        File localPath = new File(repositoryPath);
        Repository repository = FileRepositoryBuilder.create(new File(localPath, ".git"));
        repository.create();
        return repository;
    }

    /**
     * 下载一个远程仓库 需要登录鉴权
     *
     * @param gitUsername 账户
     * @param gitPassword 密码
     * @param gitUrl      git 地址
     * @param gitBranch   git 分支
     * @param localPath   本地存放路径 必须给定最后一层的项目目录
     * @return 是否成功
     */
    public static Boolean cloneRepository(String gitUsername, String gitPassword, String gitUrl, String gitBranch, String localPath) {
        boolean flag = true;
        try {
            // 设置远程服务器上的用户名和密码
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            // 设置远程URI
            Git git = Git.cloneRepository().setURI(gitUrl)
                    // 设置clone下来的分支,默认master
                    .setBranch(gitBranch)
                    // 设置下载存放路径
                    .setDirectory(new File(localPath))
                    // 设置权限验证
                    .setCredentialsProvider(provider).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 下载一个远程仓库 不需要登录鉴权
     *
     * @param gitUrl    git 地址
     * @param gitBranch git 分支
     * @param localPath 本地存放路径 必须给定最后一层的项目目录
     * @return 是否成功
     */
    public static Boolean cloneRepository(String gitUrl, String gitBranch, String localPath) {
        boolean flag = true;
        try {
            // 设置远程URI
            Git git = Git.cloneRepository().setURI(gitUrl)
                    // 设置clone下来的分支,默认master
                    .setBranch(gitBranch)
                    // 设置下载存放路径
                    .setDirectory(new File(localPath)).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 创建一个新的分支并推送到远程仓库 不需要登录远程仓库的权限
     * 如果分支存在会先删除在创建
     *
     * @param gitPath    本地 git 仓库路径
     * @param branchName 分支名称
     * @return 是否成功
     */
    public static Boolean newBranch(String gitPath, String branchName) {
        gitPath += File.separator + ".git";
        String newBranchIndex = "refs/heads/" + branchName;
        try (Git git = Git.open(new File(gitPath))) {
            // 检查新建的分支是否已经存在，如果存在则将已存在的分支强制删除并新建一个分支
            List<Ref> refs = git.branchList().call();
            for (Ref ref : refs) {
                if (ref.getName().equals(newBranchIndex)) {
                    System.out.println("Removing branch before");
                    git.branchDelete().setBranchNames(branchName).setForce(true).call();
                    break;
                }
            }
            // 新建分支
            Ref ref = git.branchCreate().setName(branchName).call();
            // 推送到远程
            git.push().add(ref).call();
            return true;
        } catch (GitAPIException | IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建一个新的分支并推送到远程仓库 需要登录远程仓库的权限
     * 如果分支存在会先删除在创建
     *
     * @param gitUsername 用户名
     * @param gitPassword 密码
     * @param gitPath     本地 git 仓库路径
     * @param branchName  分支名称
     * @return 是否成功
     */
    public static Boolean newBranch(String gitUsername, String gitPassword, String gitPath, String branchName) {
        gitPath += File.separator + ".git";
        String newBranchIndex = "refs/heads/" + branchName;
        try (Git git = Git.open(new File(gitPath))) {
            // 检查新建的分支是否已经存在，如果存在则将已存在的分支强制删除并新建一个分支
            List<Ref> refs = git.branchList().call();
            for (Ref ref : refs) {
                if (ref.getName().equals(newBranchIndex)) {
                    System.out.println("Removing branch before");
                    git.branchDelete().setBranchNames(branchName).setForce(true).call();
                    break;
                }
            }
            // 新建分支
            Ref ref = git.branchCreate().setName(branchName).call();
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            // 推送到远程
            git.push().add(ref).setCredentialsProvider(provider).call();
            return true;
        } catch (GitAPIException | IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 添加文件
     *
     * @param gitPath  本地 git 仓库路径
     * @param fileName 文件名
     * @return 是否成功
     */
    public static boolean addFile(String gitPath, String fileName) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.add().addFilepattern(fileName).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 添加文件
     *
     * @param gitPath 本地 git 仓库路径
     * @return 是否成功
     */
    public static boolean addAllFile(String gitPath) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.add().addFilepattern(".").call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 提交代码到本地仓库
     *
     * @param gitPath 本地 git 仓库路径
     * @param comment 提交 git 内容描述
     * @return 是否成功
     */
    public static boolean commitFile(String gitPath, String comment) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            // 提交代码到本地仓库
            git.commit().setMessage(comment).call();
            System.out.println("Committed to repository at " + git.getRepository().getDirectory());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            System.out.println("commitFile error! \n" + e.getMessage());
        }
        return flag;
    }

    /**
     * 推送代码到远程仓库 不需要登录远程仓库的权限
     *
     * @param gitPath 本地 git 仓库路径
     * @return 是否成功
     */
    public static boolean push(String gitPath) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            // 提交代码到本地仓库
            git.push().call();
            System.out.println("push " + git.getRepository() + File.separator + git.getRepository().getBranch());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            System.out.println("push error! \n" + e.getMessage());
        }
        return flag;
    }

    /**
     * 推送代码到远程仓库 需要登录远程仓库的权限
     *
     * @param gitPath 本地 git 仓库路径
     * @return 是否成功
     */
    public static boolean push(String gitUsername, String gitPassword, String gitPath) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            // 提交代码到本地仓库
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            git.push().setCredentialsProvider(provider).call();
            System.out.println("push " + git.getRepository() + File.separator + git.getRepository().getBranch());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            System.out.println("push error! \n" + e.getMessage());
        }
        return flag;
    }

    /**
     * 提交并推送代码至远程服务器 不需要登录远程仓库的权限
     *
     * @param filePath 提交文件路径(相对路径)
     * @param desc     提交描述
     * @return
     */
    public static boolean commitAndPush(String gitPath, String filePath, String desc) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.add().addFilepattern(filePath).call();
            //提交
            git.commit().setMessage(desc).call();
            //推送到远程
            git.push().call();
            System.out.println("Commit And Push file " + filePath + " to repository at " + git.getRepository().getDirectory());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            System.out.println("Commit And Push error! \n" + e.getMessage());
        }
        return flag;
    }

    /**
     * 提交并推送代码至远程服务器 需要登录远程仓库的权限
     *
     * @param gitUsername 用户名
     * @param gitPassword 密码
     * @param filePath    提交文件路径(相对路径)
     * @param desc        提交描述
     * @return
     */
    public static boolean commitAndPush(String gitUsername, String gitPassword, String gitPath, String filePath, String desc) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.add().addFilepattern(filePath).call();
            //提交
            git.commit().setMessage(desc).call();
            //推送到远程
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            git.push().setCredentialsProvider(provider).call();
            System.out.println("Commit And Push file " + filePath + " to repository at " + git.getRepository().getDirectory());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            System.out.println("Commit And Push error! \n" + e.getMessage());
        }
        return flag;
    }

    /**
     * 从远程仓库拉取最新代码 不需要登录远程仓库的权限
     *
     * @param gitPath          本地 git 仓库路径
     * @param remoteBranchName 分支名称
     * @return 是否成功
     */
    public static boolean pull(String gitPath, String remoteBranchName) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.pull().setRemoteBranchName(remoteBranchName).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 从远程仓库拉取最新代码 需要登录远程仓库的权限
     *
     * @param gitUsername      用户名
     * @param gitPassword      密码
     * @param gitPath          本地 git 仓库路径
     * @param remoteBranchName 分支名称
     * @return 是否成功
     */
    public static boolean pull(String gitUsername, String gitPassword, String gitPath, String remoteBranchName) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            git.pull().setRemoteBranchName(remoteBranchName).setCredentialsProvider(provider).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 从远程获取最新版本到本地   不会自动合并 merge 不需要登录远程仓库的权限
     *
     * @param gitPath 本地 git 仓库路径
     * @return 是否成功
     */
    public static boolean fetch(String gitPath) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            git.fetch().setCheckFetchedObjects(true).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 从远程获取最新版本到本地   不会自动合并 merge 需要登录远程仓库的权限
     *
     * @param gitUsername 用户名
     * @param gitPassword 密码
     * @param gitPath     本地 git 仓库路径
     * @return 是否成功
     */
    public static boolean fetch(String gitUsername, String gitPassword, String gitPath) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath))) {
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            git.fetch().setCheckFetchedObjects(true).setCredentialsProvider(provider).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    /**
     * 拉取新创建的分支到本地
     *
     * @param cloneURL 远程仓库地址
     * @param gitPath  本地 git 仓库路径
     * @return 是否成功
     */
    @SuppressWarnings("static-access")
    public static boolean pullNewBranchToLocal(String cloneURL, String gitPath) {
        String configPath = gitPath + File.separator + ".git";
        boolean flag = false;
        String[] splitURL = cloneURL.split(" ");
        String branchName = splitURL[1];
        String fileDir = gitPath + "/" + branchName;
        // 检查目标文件夹是否存在
        File file = new File(fileDir);
        if (file.exists()) {
            deleteFolder(file);
        }
        Git git;
        try {
            git = Git.open(new File(configPath));
            git.cloneRepository().setURI(cloneURL).setDirectory(file).call();
            flag = true;
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 拉取新创建的分支到本地
     *
     * @param gitUsername 用户名
     * @param gitPassword 密码
     * @param cloneURL    远程仓库地址
     * @param gitPath     本地 git 仓库路径
     * @return 是否成功
     */
    @SuppressWarnings("static-access")
    public static boolean pullNewBranchToLocal(String gitUsername, String gitPassword, String cloneURL, String gitPath) {
        String configPath = gitPath + File.separator + ".git";
        boolean flag = false;
        String[] splitURL = cloneURL.split(" ");
        String branchName = splitURL[1];
        String fileDir = gitPath + "/" + branchName;
        // 检查目标文件夹是否存在
        File file = new File(fileDir);
        if (file.exists()) {
            deleteFolder(file);
        }
        Git git;
        try {
            git = Git.open(new File(configPath));
            UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
            git.cloneRepository().setURI(cloneURL).setDirectory(file).setCredentialsProvider(provider).call();
            flag = true;
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 切换分支
     *
     * @param gitPath    本地 git 仓库路径
     * @param branchName 分支名称
     * @return 是否成功
     */
    public static boolean checkout(String gitPath, String branchName) {
        boolean flag = true;
        try (Git git = Git.open(new File(gitPath));) {
            git.checkout().setName("refs/heads/" + branchName).setForce(true).call();
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }

    private static void deleteFolder(File file) {
        if (file.isFile() || file.list().length == 0) {
            file.delete();
        } else {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                deleteFolder(files[i]);
                files[i].delete();
            }
        }
    }

}
